/*
 * Design_Assignment8.c
 *
 * Created: 4/30/2014 6:44:14 AM
 *  Author: Jared
 *
 *	Write a C program that will interface and MCP 4725 I2C DAC breakout board to
 * generate at sine wave. Verify the sine wave in the oscilloscope. Connect a
 * potentiometer to an analog port and vary the frequency based on the analog value.
 *
 */ 
 
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#define TRUE		1
#define FALSE		0

volatile uint16_t DACout = 0;					// Output value for DAC
volatile uint8_t phase = 0;						// Phase of the sine wave
volatile uint8_t ticks = 0;						// Number of ticks that have occurred since the last transition
volatile uint8_t DACin = 0;						// Value to the DAC before it has been transformed
volatile uint8_t delta = 20;					// When the timer interrupts the number of ticks to send.

static const uint8_t sine[] PROGMEM =
{
	40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,42,42,
	42,42,42,42,42,43,43,43,43,43,44,44,44,44,45,45,45,46,46,46,46,47,47,48,48,48,49,49,50,50,51,51,
	52,52,53,54,54,55,56,57,58,58,59,61,62,63,64,65,67,69,70,72,74,77,79,82,85,89,94,99,105,112,122,135,
	152,180,235
};

void twi_init(void) {
       TWBR = 80;
       TWSR &= ~(1<<TWPS1) | ~(1<<TWPS0);								// Prescaler as 1
 
       /* F_SCK = F_CPU / (16 + 2*(TWBR)*POW(4,TWPS)); */
 }

int twi_send(unsigned char addr, unsigned char value)
{
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);							// First send start condition over bus
	while(!(TWCR & (1<<TWINT)));										// Wait for it to get done
	if((TWSR & 0xF8) != 0x08)											// Check if Start was transmitted or not
	return FALSE;
																		// Start transmitted now transmit SLA+W
	TWDR = addr;														// 1100 000 0 = 0xc0 for MCP4725
	TWCR = (1<<TWINT) | (1<<TWEN);										// Clear TWINT flag to start transmission
	while(!(TWCR & (1<<TWINT)));										// Wait for it to get done
	if((TWSR & 0xF8)!= 0x18)											// Check if transmitted successfully
	return FALSE;
	
	TWDR = 0x60;														// Load first data byte into TWDR	
	TWCR = (1<<TWINT) | (1<<TWEN);										// Clear TWINT flag to start transmission
	while(!(TWCR & (1<<TWINT)));										// Wait for it to get done
	if((TWSR & 0xF8)!= 0x28)											// Check if transmitted successfully
	return FALSE;
	
	TWDR =value;														// Load first data byte into TWDR
	TWCR = (1<<TWINT) | (1<<TWEN);										// Clear TWINT flag to start transmission
	while(!(TWCR & (1<<TWINT)));										// Wait for it to get done
	if((TWSR & 0xF8)!= 0x28)											// Check if transmitted successfully
	return FALSE;
	
	TWDR = 0xF0;														// Load first data byte into TWDR
	TWCR = (1<<TWINT) | (1<<TWEN);										// Clear TWINT flag to start transmission
	while(!(TWCR & (1<<TWINT)));										// Wait for it to get done
	if((TWSR & 0xF8)!= 0x28)											// Check if transmitted successfully
	return FALSE;
	
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);							// Now transmit STOP condition
	return TRUE;	
}

//======================================== Main Function ========================================

int main(void)
{
// ---------Start ADC--------
	ADCSRA |= (1<<ADEN);												// Enable ADC
	ADMUX |= (1<<REFS0);												// Ext. Cap at AREF pin
	ADCSRA |= (1<<ADSC);												// Start Conversion

// ----Set Interrupt Timer----
	TCCR1A = 0b00000000;
	TCCR1B |= (1<<WGM12) | (1<<CS10);									// CTC mode, no prescaling
	TIMSK1 |= (1<<OCIE1A);												// OCF1A Interrupt compare match
	OCR1A = 250;
	sei();																// Enable Interrupts
	
// ----Output to MCP4725----	
	twi_init();															// Calls the Initialize TWI function
	while(1){															// Continuously output to MCP
		cli();															// Atomically read from the global DAC value.
		uint16_t SWave = DACout;
		sei();
		twi_send((uint8_t)((SWave>>8)&0x0F),(uint8_t)(SWave&0xFF));		// Write to the MCP4725.
	}
}

//============= Interrupt Subroutine: Adjusts sine wave frequency with potentiometer change =============

ISR(TIMER1_COMPA_vect){
	cli();							// Output to MCP
	if(!(ADCSRA&(1<<ADSC))){		// Checks if the ADC has finished
		delta = 8+(ADC>>4);			// Sets the number of ticks to increment so the frequency varies.
		ADCSRA |= (1<<ADEN);		// Starts the ADC again.
		ADMUX |= (1<<REFS0);		// Ext. Capacitor at AREF pin
		ADCSRA |= (1<<ADSC);		// Start conversion
	}

// ----Read from the inverse sine and adjust it with the phase----
	uint8_t d = pgm_read_word(&sine[(phase&1)?98-DACin:DACin]);	
	if(ticks+delta < d && ticks < d-delta){
		ticks += delta;
	}
	else{
		ticks += delta;
		
// ----Increment the states. Skip states if potentiometer change is too large----
		do{
			ticks -= d;
			DACin++;
			if(DACin >= 99){											// Updates phase only if needed.
				DACin = 0;
				phase = (phase+1)&3;
			}
			d = pgm_read_word(&sine[(phase&1)?98-DACin:DACin]);			// Inverse the sine.
		}
		while(ticks > d);
		uint16_t scale = DACin;
		
// ----Transforms the output based on scale and phase----		
		switch(phase){							
			case 0: scale += 99; break;
			case 1: scale = 198-scale; break;
			case 2: scale = 99-scale; break;
		}
		scale <<= 4;
		DACout = scale;
	}
	sei();
}